///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#include "ExtrasPrerequisites.h "

#include "PanelOverlayElement.h"

namespace Extras {
	
    class BorderRenderable;
    
    class _PhiloExtrasExport BorderPanelOverlayElement : public PanelOverlayElement
    {
        friend class BorderRenderable;
    public:
        
        BorderPanelOverlayElement(const Ogre::String& name);
        virtual ~BorderPanelOverlayElement();

        virtual void initialise(void);

		const Ogre::String& getTypeName(void) const;
        
        void setBorderSize(Real size);

        void setBorderSize(Real sides, Real topAndBottom);

        void setBorderSize(Real left, Real right, Real top, Real bottom);

        Real getLeftBorderSize(void) const;
        
        Real getRightBorderSize(void) const;
        
        Real getTopBorderSize(void) const;
        
        Real getBottomBorderSize(void) const;

        void setLeftBorderUV(Real u1, Real v1, Real u2, Real v2);
        
        void setRightBorderUV(Real u1, Real v1, Real u2, Real v2);
        
        void setTopBorderUV(Real u1, Real v1, Real u2, Real v2);
        
        void setBottomBorderUV(Real u1, Real v1, Real u2, Real v2);
        
        void setTopLeftBorderUV(Real u1, Real v1, Real u2, Real v2);
        
        void setTopRightBorderUV(Real u1, Real v1, Real u2, Real v2);
        
        void setBottomLeftBorderUV(Real u1, Real v1, Real u2, Real v2);
        
        void setBottomRightBorderUV(Real u1, Real v1, Real u2, Real v2);

		Ogre::String getLeftBorderUVString() const;
		Ogre::String getRightBorderUVString() const;
		Ogre::String getTopBorderUVString() const;
		Ogre::String getBottomBorderUVString() const;
		Ogre::String getTopLeftBorderUVString() const;
		Ogre::String getTopRightBorderUVString() const;
		Ogre::String getBottomLeftBorderUVString() const;
		Ogre::String getBottomRightBorderUVString() const;

        void setBorderMaterialName(const Ogre::String& name);
        
        const Ogre::String& getBorderMaterialName(void) const;

        void _updateRenderQueue(RenderQueue* queue);
		/// @copydoc OvelayElement::visitRenderables
		void visitRenderables(Renderable::Visitor* visitor, 
			bool debugRenderables = false);

        void setMetricsMode(GuiMetricsMode gmm);

        void _update(void);

        class CmdBorderSize : public ParamCommand
        {
        public:
            Ogre::String doGet(const void* target) const;
            void doSet(void* target, const Ogre::String& val);
        };
        
        class CmdBorderMaterial : public ParamCommand
        {
        public:
            Ogre::String doGet(const void* target) const;
            void doSet(void* target, const Ogre::String& val);
        };
        
        class CmdBorderLeftUV : public ParamCommand
        {
        public:
            Ogre::String doGet(const void* target) const;
            void doSet(void* target, const Ogre::String& val);
        };
        
        class CmdBorderTopUV : public ParamCommand
        {
        public:
            Ogre::String doGet(const void* target) const;
            void doSet(void* target, const Ogre::String& val);
        };
        
        class CmdBorderRightUV : public ParamCommand
        {
        public:
            Ogre::String doGet(const void* target) const;
            void doSet(void* target, const Ogre::String& val);
        };
        
        class CmdBorderBottomUV : public ParamCommand
        {
        public:
            Ogre::String doGet(const void* target) const;
            void doSet(void* target, const Ogre::String& val);
        };
        
        class CmdBorderTopLeftUV : public ParamCommand
        {
        public:
            Ogre::String doGet(const void* target) const;
            void doSet(void* target, const Ogre::String& val);
        };
        
        class CmdBorderBottomLeftUV : public ParamCommand
        {
        public:
            Ogre::String doGet(const void* target) const;
            void doSet(void* target, const Ogre::String& val);
        };
        
        class CmdBorderBottomRightUV : public ParamCommand
        {
        public:
            Ogre::String doGet(const void* target) const;
            void doSet(void* target, const Ogre::String& val);
        };
        
        class CmdBorderTopRightUV : public ParamCommand
        {
        public:
            Ogre::String doGet(const void* target) const;
            void doSet(void* target, const Ogre::String& val);
        };
    protected:
        Real mLeftBorderSize;
        Real mRightBorderSize;
        Real mTopBorderSize;
        Real mBottomBorderSize;
		struct CellUV {
			Real u1, v1, u2, v2;
		};
		CellUV mBorderUV[8];

        ushort mPixelLeftBorderSize;
        ushort mPixelRightBorderSize;
        ushort mPixelTopBorderSize;
        ushort mPixelBottomBorderSize;

        Ogre::String mBorderMaterialName;
        MaterialPtr mBorderMaterial;

        // Render operation for the border area
        RenderOperation mRenderOp2;

        static Ogre::String msTypeName;

        /// internal method for setting up geometry, called by OverlayElement::update
        void updatePositionGeometry(void);
		/// internal method for setting up geometry, called by OverlayElement::update
		void updateTextureGeometry(void);
        /// Internal method for setting up parameters
        void addBaseParameters(void);

        enum BorderCellIndex {
            BCELL_TOP_LEFT = 0,
            BCELL_TOP = 1,
            BCELL_TOP_RIGHT = 2,
            BCELL_LEFT = 3,
            BCELL_RIGHT = 4,
            BCELL_BOTTOM_LEFT = 5,
            BCELL_BOTTOM = 6,
            BCELL_BOTTOM_RIGHT = 7
        };
	    Ogre::String getCellUVString(BorderCellIndex idx) const;

        // Command objects
        static CmdBorderSize msCmdBorderSize;
        static CmdBorderMaterial msCmdBorderMaterial;
        static CmdBorderLeftUV msCmdBorderLeftUV;
        static CmdBorderTopUV msCmdBorderTopUV;
        static CmdBorderBottomUV msCmdBorderBottomUV;
        static CmdBorderRightUV msCmdBorderRightUV;
        static CmdBorderTopLeftUV msCmdBorderTopLeftUV;
        static CmdBorderBottomLeftUV msCmdBorderBottomLeftUV;
        static CmdBorderTopRightUV msCmdBorderTopRightUV;
        static CmdBorderBottomRightUV msCmdBorderBottomRightUV;

        BorderRenderable* mBorderRenderable;
    };

    
    class _PhiloExtrasExport BorderRenderable : public Renderable, public MemAlloc_SceneObj
    {
    protected:
        BorderPanelOverlayElement* mParent;
    public:
        
        BorderRenderable(BorderPanelOverlayElement* parent) : mParent(parent)
        {
            mUseIdentityProjection = true;
            mUseIdentityView = true;
        }
        const MaterialPtr& getMaterial(void) const { return mParent->mBorderMaterial; }
        void getRenderOperation(RenderOperation& op) { op = mParent->mRenderOp2; }
        void getWorldTransforms(Matrix4* xform) const { mParent->getWorldTransforms(xform); }
        unsigned short getNumWorldTransforms(void) const { return 1; }
        Real getSquaredViewDepth(const Camera* cam) const { return mParent->getSquaredViewDepth(cam); }
        const LightList& getLights(void) const
        {
            // N/A, panels are not lit
            static LightList ll;
            return ll;
        }
		bool getPolygonModeOverrideable(void) const
		{
			return mParent->getPolygonModeOverrideable();
		}
    };

}

